
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Future;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import monitoreo.Estadistica;
import monitoreo.Monitoreable;
import monitoreo.Resumen;

/**
 *
 * @author Osvaldo Clua
 * @changes Czelada
 */
class Lanzador {

    protected static long interpretar(String arg, long omision, String msg) {
        long valor = omision;
        try {
            valor = Long.parseLong(arg);
        } catch (NumberFormatException e) {
            System.err.println(msg);
            System.exit(1);
        }
        return valor;
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        /**
            args[0] => muestras           
            args[1] => cantParalelizable
            args[2] => cantSecuencial
            args[3] => op
            args[4] => nivel de mensajes.
            args[4...] => secuencia de cantParalelizableidad de hilos. No comprueba repetidos.
        **/

        long muestras = interpretar(args[0], 30,"Cantidad de muestras incorrectas."); // cantParalelizableidad de muestras por cada cantParalelizableidad de threads. Al menos 30 muestras para que el analisis por distribucion normal sea estadisticamente correcto.
        long cantParalelizable = interpretar(args[1], 420000000, "Repeticiones paralelizables incorrectas."); //42 millones para que sea multiplo de 3
        long cantSecuencial = interpretar(args[2], 0, "Repeticiones secuencial incorrectas."); // 0 quiere decir sin parte secuencial.

        String operacion = args[3];
        long verbose = interpretar(args[4], 0, "El nivel de verbosidad no es un entero.");

        ArrayList<Integer> ths = new ArrayList<Integer>();
        for (int i = 5; i<args.length; i++) {
            int valor = (int)interpretar(args[i],0,"La lista de hilos esta mal definida");
            ths.add( valor );
        }

        String BASE = "fiuba";
        long SEMILLA = 0;
        boolean debug = verbose > 0;
                
        if (debug)
            System.out.println("Inicializacion");
        Estadistica estAux = new Estadistica();

//        List<Long> listaAux = new LinkedList<Long>();
        Resumen resumen = new Resumen();

        Tarea init = null;
        int initN = 0;
        if (operacion.compareTo("SUMAS") == 0) {
            init = new Sumas("Sumador", estAux, 1, cantParalelizable, resumen, debug); //listaAux);
            initN = 5;
        } else if (operacion.compareTo("RAIZ") == 0) {
            init = new Raiz("Raiz", estAux, 1, cantParalelizable, SEMILLA, resumen, debug); //listaAux);
            initN = 2;
        }else if (operacion.compareTo("STRINGS") == 0){
            init = new PermutarString("Strings", estAux, 1, cantParalelizable, BASE, resumen, debug); //listaAux);
            initN = 2;
        }else if (operacion.compareTo("NULA") == 0) {
            init = new Nula("Nula", estAux, resumen, debug); //listaAux);
            initN = 0;
        }   
        for (int i = 0; i<initN; i++) {
//            listaAux.clear();
            init.run();
        }
        resumen = null;
        if (debug)
            System.out.println("Fin de Inicializacion");

        DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
        Date date = new Date();
        String arch = operacion+".";
        FileWriter fw = new FileWriter(arch + muestras +".csv", false);
        PrintWriter pw = new PrintWriter(fw);
        pw.println("#Hilos,Muestras,,,,,Global,,,,,");
        pw.println("#Hilos,Minimo,Maximo,Media,Varianza,Cantidad,Minimo,Maximo,Media,Varianza,Cantidad");
        for (Integer ithreads : ths) {
            if (debug)
                System.out.println("Comienzo " + dateFormat.format(date) + " " + ithreads + " threads");
            long delta = cantParalelizable / ithreads;  // cuantos calcula cada thread
            long inicio = 1;
            long fin = delta;

            resumen = new Resumen();
            Resumen resGlobal = new Resumen();
            for (int veces = 1; veces <= muestras; veces++) {
                Estadistica es[] = new Estadistica[ithreads];
                Tarea task[] = new Tarea[ithreads];

                List< Future<?> > f = new LinkedList< Future<?> >();
                List<Long> lista = new LinkedList<Long>();

                for (int ii = 0; ii < ithreads; ii++) {
                    es[ii] = new Estadistica();

                    if (operacion.compareTo("SUMAS") == 0)
                        task[ii] = new Sumas("Sumador " + ii, es[ii], inicio, inicio+delta-1, resumen, debug);
                    else if (operacion.compareTo("RAIZ") == 0)
                        task[ii] = new Raiz("Raiz "+ ii, es[ii], inicio, inicio+delta-1, SEMILLA, resumen, debug);
                    else if (operacion.compareTo("STRINGS") == 0)
                        task[ii] = new PermutarString("String "+ ii, es[ii], inicio, inicio+delta-1, BASE, resumen, debug);
                    else if (operacion.compareTo("NULA") == 0)
                        task[ii] = new Nula("Nula"+ ii, es[ii], resumen, debug); //listaAux);
                    else {
                        System.err.println("Operacion no definida.");
                        System.exit(1);
                    }
                    inicio = inicio + delta;
                }

                ExecutorService t = Executors.newFixedThreadPool(ithreads);

                Estadistica estGlobal = new Estadistica();
                Monitoreable global = new Monitoreable("Global",estGlobal);
                global.startMonitor();

//                TODO Agregar tarea secuencial.

                Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
                for (int ii = 0; ii < ithreads; ii++) {
                    f.add( t.submit(task[ii]) );
                }

                t.shutdown(); // No se aceptan mas tareas.
                t.awaitTermination(1200, TimeUnit.SECONDS); // Espero una hora (infinito)
                global.endMonitor();
                resGlobal.actualizar(estGlobal.getT());
            }

            pw.println(ithreads+","+resumen+","+resGlobal);

            date = new Date();
            if (debug)
                System.out.println("Final " + dateFormat.format(date) + " " + ithreads + " threads"); 
        }


        pw.close();
        fw.close();
    }
}
